rules TupleMath {
    // add tuples componentwise
    (${a: (Int, Int)} + ${n: (Int, Int)}) => ($a[0] + $n[0], $a[1] + $n[1]);
}

function main() {
    var x: (Int, CString, CString) = (1, "second value", "third value");
    var y: (CString, Int) = ("something", 2);
    var z: (CString, Int) = ("something", 2);

    var a: Int;
    var b: CString;
    (a, b, _) = x;
    printf("%i\n", a);
    printf("%s\n", b);
    printf("%s\n", x[2]);

    var c: CString;
    var d: Int;
    (c, d) = ("something", 3);
    printf("%s\n", c);
    printf("%i\n", d);

    var e = (1, ("inner slot 1", "inner slot 2"));
    var f: Int;
    var g: CString;
    var h: CString;
    (f, (g, h)) = e;
    printf("%i\n", f);
    printf("%s\n", g);
    printf("%s\n", h);

    // let's go nuts here
    var i: (Int, (Int, (Int, Int), (Int, Int, Int), (Int, (Int, Int), (Int, Int)))) = (1, (1, (1, 1), (1, 1, 2), (1, (1, 1), (1, 1))));
    var j: Int;
    (_, (_, (_, _), (_, _, j), (_, (_, _), (_, _)))) = i;
    printf("%i\n", j);
    printf("%i\n", i[1][2][2]);

    var a2: (Int, Float, CString) = (1, 2, "hello!");
    var b2: Int;
    (b2, _, _) = a2;
    printf("%.2f\n", a2[1]);

    using rules TupleMath {
        var a = (1_i, 2_i) + (3_i, 4_i);
        printf("(%i, %i)\n", a[0], a[1]);
    }

    // this could probably be cleaned up by supporting tuples in autorefderef
    var d2 = (2, "hello");
    var c2: (Int, Ptr[(Int, CString)]) = (1, &d2);
    printf("%s\n", (*c2[1])[1]);

    // TODO: tuple equality
}
